package com.seafood.service.impl;

import com.seafood.dto.SendSmsRequestDTO;
import com.seafood.dto.SmsVerificationCodeDTO;
import com.seafood.dto.VerifySmsCodeRequestDTO;
import com.seafood.entity.SmsVerificationCode;
import com.seafood.repository.SmsVerificationCodeRepository;
import com.seafood.service.SmsVerificationCodeService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 短信验证码服务实现
 */
@Slf4j
@Service
@RequiredArgsConstructor
@Transactional
public class SmsVerificationCodeServiceImpl implements SmsVerificationCodeService {
    
    private final SmsVerificationCodeRepository smsRepository;
    
    private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
    
    // 配置常量
    private static final int CODE_LENGTH = 6;
    private static final int CODE_EXPIRE_MINUTES = 5;
    private static final int MAX_SEND_PER_HOUR = 5;
    private static final int MAX_SEND_PER_DAY = 20;
    private static final int MAX_IP_SEND_PER_HOUR = 50;
    
    @Override
    public SmsVerificationCodeDTO sendVerificationCode(SendSmsRequestDTO requestDTO, String ipAddress, String userAgent) {
        String phone = requestDTO.getPhone();
        String codeType = requestDTO.getCodeType();
        
        // 检查发送频率限制
        if (!checkSendFrequency(phone, codeType, ipAddress)) {
            throw new RuntimeException("发送验证码过于频繁，请稍后再试");
        }
        
        // 生成验证码
        String code = generateVerificationCode();
        
        // 创建验证码记录
        SmsVerificationCode smsCode = new SmsVerificationCode();
        smsCode.setPhone(phone);
        smsCode.setCode(code);
        smsCode.setCodeType(SmsVerificationCode.CodeType.valueOf(codeType.toUpperCase()));
        smsCode.setExpireTime(LocalDateTime.now().plusMinutes(CODE_EXPIRE_MINUTES));
        smsCode.setIpAddress(ipAddress);
        smsCode.setUserAgent(userAgent);
        
        // 保存到数据库
        SmsVerificationCode saved = smsRepository.save(smsCode);
        
        // 发送短信（这里模拟发送，实际应调用第三方SMS API）
        sendSmsMessage(phone, code, codeType);
        
        log.info("发送验证码成功: phone={}, type={}, code={}", phone, codeType, code);
        
        return convertToDTO(saved);
    }
    
    @Override
    @Transactional(readOnly = true)
    public boolean verifyCode(VerifySmsCodeRequestDTO requestDTO, String ipAddress) {
        String phone = requestDTO.getPhone();
        String code = requestDTO.getCode();
        String codeType = requestDTO.getCodeType();
        
        SmsVerificationCode.CodeType type = SmsVerificationCode.CodeType.valueOf(codeType.toUpperCase());
        Optional<SmsVerificationCode> optionalSmsCode = smsRepository.findByPhoneAndCodeAndCodeType(phone, code, type);
        
        if (optionalSmsCode.isEmpty()) {
            log.warn("验证码不存在: phone={}, code={}, type={}", phone, code, codeType);
            return false;
        }
        
        SmsVerificationCode smsCode = optionalSmsCode.get();
        
        // 增加尝试次数
        smsCode.incrementAttempts();
        smsRepository.save(smsCode);
        
        // 检查验证码是否有效
        if (!smsCode.isValid()) {
            log.warn("验证码无效: phone={}, code={}, type={}, expired={}, used={}, attempts={}", 
                    phone, code, codeType, smsCode.isExpired(), smsCode.getIsUsed(), smsCode.getAttempts());
            return false;
        }
        
        log.info("验证码验证成功: phone={}, type={}", phone, codeType);
        return true;
    }
    
    @Override
    public boolean verifyAndUseCode(VerifySmsCodeRequestDTO requestDTO, String ipAddress) {
        String phone = requestDTO.getPhone();
        String code = requestDTO.getCode();
        String codeType = requestDTO.getCodeType();
        
        SmsVerificationCode.CodeType type = SmsVerificationCode.CodeType.valueOf(codeType.toUpperCase());
        Optional<SmsVerificationCode> optionalSmsCode = smsRepository.findByPhoneAndCodeAndCodeType(phone, code, type);
        
        if (optionalSmsCode.isEmpty()) {
            return false;
        }
        
        SmsVerificationCode smsCode = optionalSmsCode.get();
        
        // 增加尝试次数
        smsCode.incrementAttempts();
        
        // 检查验证码是否有效
        if (!smsCode.isValid()) {
            smsRepository.save(smsCode);
            return false;
        }
        
        // 标记为已使用
        smsCode.markAsUsed();
        smsRepository.save(smsCode);
        
        log.info("验证码使用成功: phone={}, type={}", phone, codeType);
        return true;
    }
    
    @Override
    @Transactional(readOnly = true)
    public SmsVerificationCodeDTO getLatestCodeByPhoneAndType(String phone, String codeType) {
        SmsVerificationCode.CodeType type = SmsVerificationCode.CodeType.valueOf(codeType.toUpperCase());
        List<SmsVerificationCode> codes = smsRepository.findValidCodesByPhoneAndType(phone, type, LocalDateTime.now());
        
        if (codes.isEmpty()) {
            return null;
        }
        
        return convertToDTO(codes.get(0));
    }
    
    @Override
    @Transactional(readOnly = true)
    public boolean checkSendFrequency(String phone, String codeType, String ipAddress) {
        LocalDateTime now = LocalDateTime.now();
        SmsVerificationCode.CodeType type = SmsVerificationCode.CodeType.valueOf(codeType.toUpperCase());
        
        // 检查手机号1小时内发送次数
        Long hourlyCount = smsRepository.countByPhoneAndCodeTypeAndCreatedAtAfter(phone, type, now.minusHours(1));
        if (hourlyCount >= MAX_SEND_PER_HOUR) {
            log.warn("手机号发送频率超限: phone={}, hourlyCount={}", phone, hourlyCount);
            return false;
        }
        
        // 检查手机号1天内发送次数
        Long dailyCount = smsRepository.countByPhoneAndCodeTypeAndCreatedAtAfter(phone, type, now.minusDays(1));
        if (dailyCount >= MAX_SEND_PER_DAY) {
            log.warn("手机号日发送量超限: phone={}, dailyCount={}", phone, dailyCount);
            return false;
        }
        
        // 检查IP地址1小时内发送次数
        if (ipAddress != null) {
            Long ipHourlyCount = smsRepository.countByIpAddressAndCreatedAtAfter(ipAddress, now.minusHours(1));
            if (ipHourlyCount >= MAX_IP_SEND_PER_HOUR) {
                log.warn("IP发送频率超限: ip={}, hourlyCount={}", ipAddress, ipHourlyCount);
                return false;
            }
        }
        
        return true;
    }
    
    @Override
    @Transactional(readOnly = true)
    public List<SmsVerificationCodeDTO> getSendHistory(String phone, int limit) {
        List<SmsVerificationCode> codes = smsRepository.findByPhoneOrderByCreatedAtDesc(phone);
        return codes.stream()
                .limit(limit)
                .map(this::convertToDTO)
                .collect(Collectors.toList());
    }
    
    @Override
    public void cleanupExpiredCodes() {
        LocalDateTime cutoffTime = LocalDateTime.now().minusDays(7);
        smsRepository.deleteExpiredCodes(cutoffTime);
        log.info("清理过期验证码完成，截止时间: {}", cutoffTime);
    }
    
    @Override
    @Transactional(readOnly = true)
    public Map<String, Object> getSendStatistics(String startDate, String endDate) {
        LocalDateTime start = LocalDateTime.parse(startDate + " 00:00:00", FORMATTER);
        LocalDateTime end = LocalDateTime.parse(endDate + " 23:59:59", FORMATTER);
        
        List<Object[]> typeStats = smsRepository.countByCodeTypeAndCreatedAtAfter(start);
        
        Map<String, Object> statistics = new HashMap<>();
        Map<String, Long> typeCount = new HashMap<>();
        
        long totalCount = 0;
        for (Object[] row : typeStats) {
            String type = row[0].toString();
            Long count = (Long) row[1];
            typeCount.put(type, count);
            totalCount += count;
        }
        
        statistics.put("totalCount", totalCount);
        statistics.put("typeStatistics", typeCount);
        statistics.put("startDate", startDate);
        statistics.put("endDate", endDate);
        
        return statistics;
    }
    
    @Override
    @Transactional(readOnly = true)
    public List<Map<String, Object>> getFrequentSenders(int days, int threshold) {
        LocalDateTime startTime = LocalDateTime.now().minusDays(days);
        List<Object[]> results = smsRepository.findFrequentSenders(startTime, (long) threshold);
        
        return results.stream()
                .map(row -> {
                    Map<String, Object> sender = new HashMap<>();
                    sender.put("phone", row[0]);
                    sender.put("sendCount", row[1]);
                    return sender;
                })
                .collect(Collectors.toList());
    }
    
    @Override
    public SmsVerificationCodeDTO resendVerificationCode(String phone, String codeType, String ipAddress, String userAgent) {
        SendSmsRequestDTO requestDTO = new SendSmsRequestDTO();
        requestDTO.setPhone(phone);
        requestDTO.setCodeType(codeType);
        requestDTO.setRemark("重新发送");
        
        return sendVerificationCode(requestDTO, ipAddress, userAgent);
    }
    
    /**
     * 生成随机验证码
     */
    private String generateVerificationCode() {
        Random random = new Random();
        StringBuilder code = new StringBuilder();
        for (int i = 0; i < CODE_LENGTH; i++) {
            code.append(random.nextInt(10));
        }
        return code.toString();
    }
    
    /**
     * 发送短信消息（模拟实现）
     */
    private void sendSmsMessage(String phone, String code, String codeType) {
        // 这里应该调用真实的短信服务API
        // 例如：阿里云短信、腾讯云短信、华为云短信等
        
        String message = generateSmsMessage(code, codeType);
        log.info("模拟发送短信: phone={}, message={}", phone, message);
        
        // 实际实现示例：
        // smsClient.sendSms(phone, templateId, Map.of("code", code));
    }
    
    /**
     * 生成短信内容
     */
    private String generateSmsMessage(String code, String codeType) {
        String purpose = getCodeTypePurpose(codeType);
        return String.format("【海产品交易平台】您的%s验证码是：%s，有效期%d分钟，请勿泄露给他人。", 
                           purpose, code, CODE_EXPIRE_MINUTES);
    }
    
    /**
     * 获取验证码类型用途描述
     */
    private String getCodeTypePurpose(String codeType) {
        switch (codeType.toUpperCase()) {
            case "REGISTER": return "注册";
            case "LOGIN": return "登录";
            case "RESET_PASSWORD": return "重置密码";
            case "CHANGE_PHONE": return "修改手机号";
            case "ORDER_CONFIRM": return "订单确认";
            case "PAYMENT_CONFIRM": return "支付确认";
            case "SECURITY_VERIFY": return "安全验证";
            default: return "验证";
        }
    }
    
    /**
     * 实体转DTO
     */
    private SmsVerificationCodeDTO convertToDTO(SmsVerificationCode entity) {
        SmsVerificationCodeDTO dto = new SmsVerificationCodeDTO();
        BeanUtils.copyProperties(entity, dto);
        
        dto.setCodeType(entity.getCodeType().name());
        dto.setExpireTime(entity.getExpireTime() != null ? entity.getExpireTime().format(FORMATTER) : null);
        dto.setUsedTime(entity.getUsedTime() != null ? entity.getUsedTime().format(FORMATTER) : null);
        dto.setCreatedAt(entity.getCreatedAt() != null ? entity.getCreatedAt().format(FORMATTER) : null);
        dto.setUpdatedAt(entity.getUpdatedAt() != null ? entity.getUpdatedAt().format(FORMATTER) : null);
        
        // 隐藏敏感信息
        dto.setCode(null);
        
        return dto;
    }
}